﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DiscoveryLogic.UI.Layouts.Converters;
using DiscoveryLogic.UI.Layouts.Models;

namespace DiscoveryLogic.UI.Layouts.Controls
{
    public partial class RetirementLineChart : UserControl
    {
        #region Private Variables

            private double[] income;
            private double[] essentialExpenses;
            private double[] discretionaryExpenses;

            private Ellipse[] incomePoints;
            private Line[] incomeLines;
            private Polygon incomeArea;
            private Ellipse[] essentialExpensesPoints;
            private Line[] essentialExpensesLines;
            private Polygon problemArea;
            private Ellipse[] discretionaryExpensesPoints;
            private Line[] discretionaryExpensesLines;

            private int yearsRange;
            private int yMinValue, yMaxValue;
            private int xMinValue, xMaxValue;

            private RetirementPlan retirementPlan;

        #endregion

        #region Constructor

            public RetirementLineChart(RetirementPlan plan)
            {
                // Required to initialize variables
                InitializeComponent();

                ChartAmountMinLabel.Text = ((double)this.Resources["Res_ChartAmountMinLabel"]).ToCurrency();

                // Add income polygon
                incomeArea = new Polygon();

                LinearGradientBrush brush = new LinearGradientBrush();
                brush.EndPoint = new Point(-0.133, -0.143);
                brush.StartPoint = new Point(1.1, 0.998);

                GradientStop g1 = new GradientStop();
                g1.Offset = 0;
                g1.Color = Utils.HexToColor("#FF009933");
                brush.GradientStops.Add(g1);

                GradientStop g2 = new GradientStop();
                g2.Offset = 1;
                g2.Color = Utils.HexToColor("#555AFF52");
                brush.GradientStops.Add(g2);
                incomeArea.Fill = brush;

                LineChartCanvas.Children.Add(incomeArea);

                // Add problem polygon
                problemArea = new Polygon();

                brush = new LinearGradientBrush();
                brush.EndPoint = new Point(-0.133, -0.143);
                brush.StartPoint = new Point(1.1, 0.998);

                g1 = new GradientStop();
                g1.Offset = 0;
                g1.Color = Utils.HexToColor("#FFF5B800");
                brush.GradientStops.Add(g1);

                g2 = new GradientStop();
                g2.Offset = 1;
                g2.Color = Utils.HexToColor("#55FFE38F");
                brush.GradientStops.Add(g2);
                problemArea.Fill = brush;

                LineChartCanvas.Children.Add(problemArea);

                retirementPlan = plan;
                retirementPlan.planChange += new EventHandler(OnPlanChange);
            }


            public RetirementLineChart(int rangeYears, RetirementPlan plan) : 
                this(plan)
            {                
                income = new double[rangeYears];
                essentialExpenses = new double[rangeYears];
                discretionaryExpenses = new double[rangeYears];

                xMaxValue = rangeYears;
            }

        #endregion

        #region Public Properties

            public double[] Income
            {
                get { return income; }
                set { income = value; }
            }

            public double[] EssentialExpenses
            {
                get { return essentialExpenses; }
                set { essentialExpenses = value; }
            }

            public double[] DiscretionaryExpenses
            {
                get { return discretionaryExpenses; }
                set { discretionaryExpenses = value; }
            }

            public int YearsRange
            {
                get { return yearsRange; }
                set { yearsRange = value; }
            }

        #endregion

        #region Event Handlers

            private void OnPlanChange(object sender, EventArgs e)
            {
                int range = retirementPlan.PlanRange;

                if (range == 0)
                {
                    return;
                }


                CleanChart();

                essentialExpenses = retirementPlan.EssentialExpensesArray;
                discretionaryExpenses = retirementPlan.DiscretionaryExpensesArray;
                income = retirementPlan.IncomeArray;
                
                essentialExpensesPoints = new Ellipse[range];
                essentialExpensesLines = new Line[range];
                discretionaryExpensesPoints = new Ellipse[range];
                discretionaryExpensesLines = new Line[range];
                incomePoints = new Ellipse[range];
                incomeLines = new Line[range];

                double xAxisLeft = (double)xAxis.GetValue(Canvas.LeftProperty) + 2;
                double yAxisTop = (double)yAxis.GetValue(Canvas.TopProperty) - 2;

                xMaxValue = retirementPlan.PlanToAge;
                xMinValue = retirementPlan.RetirementAge;
                yMinValue = 0;
                yMaxValue = Convert.ToInt32(retirementPlan.MaxDollarValue);

                double xPercent, yPercent;
                double xCoord, yCoord;

                PointCollection pc = new PointCollection();
                pc.Add(new Point(0,258.499));
                
                Ellipse dot;

                for (int i = 0; i < range; i++)
                {
                    xPercent = (i / Convert.ToDouble(range));
                    xCoord = xAxisLeft + (xPercent * xAxis.Width);


                    // plot income
                    yPercent = (income[i] - yMinValue) / (yMaxValue - yMinValue);
                    yCoord = (yAxisTop + yAxis.Height) - (yPercent * yAxis.Height);
                    //incomePoints[i] = new Point(xCoord, yCoord);
                    dot = new Ellipse();
                    dot.Height = 0;
                    dot.Width = 0;
                    dot.SetValue(Canvas.LeftProperty, xCoord);
                    dot.SetValue(Canvas.TopProperty, yCoord);
                    dot.Fill = new SolidColorBrush(Colors.Green);
                    incomePoints[i] = dot;
                    LineChartCanvas.Children.Add(dot);

                    // Add green line between dots
                    if (i != 0)
                    {
                        Line l = new Line();
                        l.X1 = (double)incomePoints[i - 1].GetValue(Canvas.LeftProperty);
                        l.Y1 = (double)incomePoints[i - 1].GetValue(Canvas.TopProperty);
                        l.X2 = xCoord;
                        l.Y2 = yCoord;
                        l.Stroke = new SolidColorBrush(Colors.Green);
                        l.StrokeThickness = 2;
                        LineChartCanvas.Children.Add(l);

                        incomeLines[i] = l;
                    }

                    pc.Add(new Point(xCoord, yCoord));


                    // plot discretionary expeneses
                    yPercent = (discretionaryExpenses[i] - yMinValue) / (yMaxValue - yMinValue);
                    yCoord = (yAxisTop + yAxis.Height) - (yPercent * yAxis.Height);
                    //discretionaryExpensesPoints[i] = new Point(xCoord, yCoord);
                    dot = new Ellipse();
                    dot.Height = 0;
                    dot.Width = 0;
                    dot.SetValue(Canvas.LeftProperty, xCoord);
                    dot.SetValue(Canvas.TopProperty, yCoord);
                    dot.Fill = new SolidColorBrush(Utils.HexToColor("#FFFCD4D8"));
                    discretionaryExpensesPoints[i] = dot;
                    LineChartCanvas.Children.Add(dot);

                    // Add pink line between dots
                    if (i != 0)
                    {
                        Line l = new Line();
                        l.X1 = (double)discretionaryExpensesPoints[i - 1].GetValue(Canvas.LeftProperty);
                        l.Y1 = (double)discretionaryExpensesPoints[i - 1].GetValue(Canvas.TopProperty);
                        l.X2 = xCoord;
                        l.Y2 = yCoord;
                        l.Stroke = new SolidColorBrush(Utils.HexToColor("#FFFCD4D8"));
                        l.StrokeThickness = 2;
                        LineChartCanvas.Children.Add(l);

                        discretionaryExpensesLines[i] = l;
                    }


                    // plot essential expenses                    
                    yPercent = (essentialExpenses[i] - yMinValue) / (yMaxValue - yMinValue);
                    yCoord = (yAxisTop + yAxis.Height) - (yPercent * yAxis.Height);
                    //essentialExpensesPoints[i] = new Point(xCoord, yCoord);
                    dot = new Ellipse();
                    dot.Height = 0;
                    dot.Width = 0;
                    dot.SetValue(Canvas.LeftProperty, xCoord);
                    dot.SetValue(Canvas.TopProperty, yCoord);
                    dot.Fill = new SolidColorBrush(Utils.HexToColor("#FFF2142B"));
                    essentialExpensesPoints[i] = dot;
                    LineChartCanvas.Children.Add(dot);

                    // Add red line between dots
                    if (i != 0)
                    {
                        Line l = new Line();
                        l.X1 = (double)essentialExpensesPoints[i - 1].GetValue(Canvas.LeftProperty);
                        l.Y1 = (double)essentialExpensesPoints[i - 1].GetValue(Canvas.TopProperty);
                        l.X2 = xCoord;
                        l.Y2 = yCoord;
                        l.Stroke = new SolidColorBrush(Utils.HexToColor("#FFF2142B"));
                        l.StrokeThickness = 2;
                        LineChartCanvas.Children.Add(l);

                        essentialExpensesLines[i] = l;
                    }
                    
                }

                // Set area below income line
                pc.Add(new Point((double)incomePoints[range-1].GetValue(Canvas.LeftProperty), 258.499));
                SetIncomeArea(pc);

                // Set income/expense labels
                IncomeLabel.SetValue(Canvas.TopProperty, (double)incomePoints[range - 1].GetValue(Canvas.TopProperty) - 12.747);
                EssentialExpensesLabel.SetValue(Canvas.TopProperty, (double)essentialExpensesPoints[range - 1].GetValue(Canvas.TopProperty) - 20.493);
                TotalExpensesLabel.SetValue(Canvas.TopProperty, (double)discretionaryExpensesPoints[range - 1].GetValue(Canvas.TopProperty) - 12.747);

                // Find areas which assets are insufficient
                FindProblemAreas();

                // Set Chart Labels Text and Positions
                double leftPositionOfLastPoint = (double)incomePoints[range - 1].GetValue(Canvas.LeftProperty);
                
                ChartAgeMinLabel.Text = retirementPlan.RetirementAge.ToString();
                
                ChartAgeMaxLabel.Text = retirementPlan.PlanToAge.ToString();                
                ChartAgeMaxLabel.SetValue(Canvas.LeftProperty, leftPositionOfLastPoint);

                ChartAmountMaxLabel.Text = Convert.ToInt32(retirementPlan.MaxDollarValue).ToCurrency();
                //ChartAmountMaxLabel.SetValue(Canvas.LeftProperty, -16.141 - (3.3 * retirementPlan.MaxDollarValue.ToString().Length));
                
                EssentialExpensesLabel.SetValue(Canvas.LeftProperty, leftPositionOfLastPoint + 5);
                TotalExpensesLabel.SetValue(Canvas.LeftProperty, leftPositionOfLastPoint + 5);
                IncomeLabel.SetValue(Canvas.LeftProperty, leftPositionOfLastPoint + 5);
            }

        #endregion

        #region Helper Methods

            private void CleanChart()
            {                    
                // Check if null
                if (incomePoints == null)
                {
                    return;
                }

                // Remove all lines and points from the chart
                for (int i = 0; i < incomePoints.Length; i++)
                {                    
                    LineChartCanvas.Children.Remove(incomePoints[i]);
                    LineChartCanvas.Children.Remove(incomeLines[i]);
                    LineChartCanvas.Children.Remove(essentialExpensesPoints[i]);
                    LineChartCanvas.Children.Remove(essentialExpensesLines[i]);
                    LineChartCanvas.Children.Remove(discretionaryExpensesPoints[i]);
                    LineChartCanvas.Children.Remove(discretionaryExpensesLines[i]);
                }                
            }

            private void SetIncomeArea(PointCollection points)
            {
                incomeArea.Points = points;
            }

            private void FindProblemAreas()
            {
                PointCollection problemAreaPoints = new PointCollection();
                double essentialExpenseTop = 0;
                double incomeTop = 0;
                int problemAreaStart = -1;
                int problemAreaEnd = -1;
                bool inProblemArea = false;
                InsufficientAssetsBalloon.Opacity = 0;

                for (int i = 0; i < essentialExpensesPoints.Length; i++)
                {
                    essentialExpenseTop = (double)essentialExpensesPoints[i].GetValue(Canvas.TopProperty);
                    incomeTop = (double)incomePoints[i].GetValue(Canvas.TopProperty);

                    if (essentialExpenseTop < incomeTop)
                    {
                        if (inProblemArea == false)
                        {
                            problemAreaStart = i;
                            inProblemArea = true;                            
                        }

                        problemAreaPoints.Add(new Point((double)essentialExpensesPoints[i].GetValue(Canvas.LeftProperty),essentialExpenseTop));
                    }
                    else if (inProblemArea)
                    {
                        inProblemArea = false;
                        problemAreaEnd = i;

                        for (int j = problemAreaEnd; j >= problemAreaStart; j--)
                        {
                            double incomePointLeft = (double)incomePoints[j].GetValue(Canvas.LeftProperty);
                            double incomePointTop = (double)incomePoints[j].GetValue(Canvas.TopProperty);
                            problemAreaPoints.Add(new Point(incomePointLeft, incomePointTop));
                        }
                    }
                }

                // At the very end, if expenses is still greater than income,
                // close the problem area polygon
                if (essentialExpenseTop < incomeTop)
                {
                    inProblemArea = false;
                    problemAreaEnd = essentialExpensesPoints.Length - 1;

                    for (int j = problemAreaEnd; j >= problemAreaStart; j--)
                    {
                        double incomePointLeft = (double)incomePoints[j].GetValue(Canvas.LeftProperty);
                        double incomePointTop = (double)incomePoints[j].GetValue(Canvas.TopProperty);
                        problemAreaPoints.Add(new Point(incomePointLeft, incomePointTop));
                    }
                }

                problemArea.Points = problemAreaPoints;

                // Show and position the insufficient assets balloon
                if (problemAreaStart != -1)
                {
                    InsufficientAssetsBalloon.Opacity = 1;
                    int middle = problemAreaStart + Convert.ToInt32((problemAreaEnd - problemAreaStart) / 2);

                    InsufficientAssetsBalloon.SetValue(Canvas.LeftProperty, (double)essentialExpensesPoints[middle].GetValue(Canvas.LeftProperty));
                    InsufficientAssetsBalloon.SetValue(Canvas.TopProperty, (double)essentialExpensesPoints[middle].GetValue(Canvas.TopProperty) + 5);
                }
            }

        #endregion


    }
}
